<template>
  <div class="vue-pdf-container" :class="{ 'is-loading': loading }">
    <!-- TOOLBAR -->
    <!-- <div class="pdf-toolbar">
      <div class="toolbar-group">
        <el-button size="small" @click="prevPage" :disabled="!canNavigate || localPage <= 1" class="flat-btn">
          <i class="fa-solid fa-arrow-left"></i>
        </el-button>

        <span class="page-info">
          第 {{ localPage }} / {{ totalPages || 0 }} 页
          <span v-if="loadingProgress > 0 && loadingProgress < 100" class="progress-text">
            ({{ loadingProgress }}%)
          </span>
        </span>

        <el-button size="small" @click="nextPage" :disabled="!canNavigate || localPage >= totalPages" class="flat-btn">
          <i class="fa-solid fa-arrow-right"></i>
        </el-button>
      </div>

      <div class="toolbar-divider" />

      <div class="toolbar-group scale-controls">
        <el-button size="small" @click="zoomOut" :disabled="!canNavigate" class="flat-btn">
          <i class="fa-solid fa-minus"></i>
        </el-button>
        <span class="scale-info">{{ Math.round(localScale * 100) }}%</span>
        <el-button size="small" @click="zoomIn" :disabled="!canNavigate" class="flat-btn">
          <i class="fa-solid fa-plus"></i>
        </el-button>
      </div> 

      <div class="toolbar-divider" />

      <div class="toolbar-group search-box">
        <el-input v-model="searchText" placeholder="搜索（回车开始）" size="small" prefix-icon="el-icon-search"
          @keyup.enter.native="searchPDF" :disabled="!canNavigate || !totalPages" clearable />
        <el-button size="small" @click="resetSearch" :disabled="!searchResults.length" class="flat-btn">
          <i class="fa-solid fa-rotate-right"></i>
        </el-button>

        <el-popover v-if="searchResults.length" placement="bottom-start" trigger="click" width="300">
          <template #reference>
            <el-button size="small" class="search-result-btn flat-btn">
              <i class="fa-solid fa-magnifying-glass"></i>
              结果 {{ searchResults.length }}
            </el-button>
          </template>
          <div class="result-list">
            <div v-for="(r, idx) in searchResults" :key="idx"
              :class="['result-item', { active: idx === currentResultIndex }]" @click="jumpToResult(idx)">
              <div class="result-page">第 {{ r.page + 1 }} 页</div>
              <div class="result-snippet" v-html="highlightMatch(r.snippet)"></div>
            </div>
          </div>
        </el-popover>
      </div>

      <div class="toolbar-right-spacer" />

      <div class="toolbar-group right-actions">
        <el-button type="primary" @click="reloadPDF" :disabled="loading">
          <i class="fa-solid fa-rotate"></i> 重新加载
        </el-button>
      </div>
    </div>
    -->

    <!-- CONTENT -->
    <el-scrollbar class="scrollable-area" :style="scrollAreaStyle">
      <div class="canvas-wrap">
        <!-- 多页 canvas: 每页一个容器 -->
        <div v-for="n in totalPages || 0" :key="n" class="page-container" :data-page="n">
          <canvas :ref="el => setCanvasRef(el, n - 1)" class="pdf-canvas" />
          <div v-if="!canvasReadyFlags[n - 1] && !errorMsg" class="canvas-placeholder">
            <el-skeleton rows="6" animated />
          </div>
        </div>

        <!-- 如果尚未初始化任何页面（totalPages=0），显示占位 -->
        <div v-if="!totalPages && !errorMsg" class="canvas-placeholder">
          <el-skeleton rows="4" animated />
        </div>
      </div>

      <div v-if="loading" class="loading-overlay">
        <div class="loading-inner">
          <el-spinner />
          <div class="loading-text">加载中... {{ loadingProgress > 0 ? loadingProgress + '%' : '' }}</div>
        </div>
      </div>

      <div v-if="errorMsg" class="error-overlay">
        <el-alert :title="errorMsg" type="error" show-icon />
        <div style="margin-top:8px;">
          <el-button size="small" type="primary" @click="reloadPDF">重试</el-button>
        </div>
      </div>
    </el-scrollbar>
  </div>
</template>

<script setup>
import { ref, watch, onMounted, onBeforeUnmount, inject, computed, nextTick } from 'vue'

const props = defineProps({
  src: { type: String, required: true },
  page: { type: Number, default: 1 },
  scale: { type: Number, default: 1.50 },
  enableHighDPI: { type: Boolean, default: true },
  // 支持 Number (像素) 或 String (例如 "100vh - 50px"，"calc(100vh - 50px)"，"80px" 等)
  scrollAreaHeight: { type: [Number, String], default: 'calc(100vh - 100px)' }
})
const emit = defineEmits(['update:page', 'update:scale', 'loaded', 'error'])

/* state */
const canvasRefs = ref([]) // array of DOM canvas elements
const canvasReadyFlags = ref([]) // per-page readiness flag
const loading = ref(false)
const loadingProgress = ref(0)
const totalPages = ref(0)
const errorMsg = ref('')
const searchText = ref('')
const searchResults = ref([])
const currentResultIndex = ref(-1)
const ownLoaded = ref(false)

/* injected */
const injectedPdfModuleRef = inject('pdfModule', null)
const injectedPdfDocRef = inject('pdfDoc', null)

/* pdf core */
let pdfModule = null
let pdfDoc = null
let loadingTask = null
let lastRenderTask = null

/* local controlled state (supports both controlled and uncontrolled usage) */
const localPage = ref(props.page)
const localScale = ref(props.scale)

/* sync props -> local when parent updates */
watch(() => props.page, (v) => {
  if (v != null && v !== localPage.value) localPage.value = v
})
watch(() => props.scale, (v) => {
  if (v != null && v !== localScale.value) localScale.value = v
})

/* when local changes, emit updates to parent (if any) */
watch(localPage, (v) => emit('update:page', v))
watch(localScale, (v) => emit('update:scale', v))

const scrollAreaStyle = computed(() => ({
  height: normalizeHeightProp(props.scrollAreaHeight),
  marginTop: '0px',
  padding: '12px',
  background: '#fafafa',
  borderRadius: '8px'
}))

const canNavigate = computed(() => !loading.value && !!totalPages.value && !errorMsg.value)

/* helper to build public url */
function buildPublicUrl(pathInPublic) {
  const base = import.meta.env?.BASE_URL ?? '/'
  const origin = window.location.origin
  const normalizedBase = base === '/' ? '' : (base.startsWith('/') ? base.replace(/\/$/, '') : '/' + base.replace(/\/$/, ''))
  const trimmedPath = pathInPublic.startsWith('/') ? pathInPublic : '/' + pathInPublic
  return origin + normalizedBase + trimmedPath
}

// 全局保存 workerUrl 以便在重试时可用
let pdfWorkerUrlGlobal = null

async function ensureModuleAndDoc() {
  try {
    loading.value = true
    loadingProgress.value = 5

    const pdfModuleUrl = buildPublicUrl('/pdfjs-dist/build/pdf.mjs')
    const pdfWorkerUrl = buildPublicUrl('/pdfjs-dist/build/pdf.worker.mjs')
    pdfWorkerUrlGlobal = pdfWorkerUrl

    const imported = await import(/* @vite-ignore */ pdfModuleUrl)
    pdfModule = imported?.default ?? imported

    // 检查 worker 可访问性
    try {
      const res = await fetch(pdfWorkerUrl, { method: 'HEAD' })
      if (!res.ok) throw new Error(`pdf.worker 无法访问，HTTP ${res.status}`)
    } catch (fetchErr) {
      throw new Error(`无法访问 pdf.worker（${pdfWorkerUrl}）：${fetchErr.message}`)
    }

    if (pdfModule && pdfModule.GlobalWorkerOptions) {
      pdfModule.GlobalWorkerOptions.workerSrc = pdfWorkerUrl
    } else if (pdfModule?.default && pdfModule.default.GlobalWorkerOptions) {
      pdfModule.default.GlobalWorkerOptions.workerSrc = pdfWorkerUrl
    } else {
      console.warn('pdfModule.GlobalWorkerOptions 不存在，pdf.js 模块可能与预期不同：', pdfModule)
    }

    window.__PDFJS_LOADED__ = { module: pdfModule }
    loadingProgress.value = 35

    loadingTask = pdfModule.getDocument({
      url: props.src,
      onProgress: (p) => {
        if (p && p.total) {
          loadingProgress.value = Math.min(100, Math.floor((p.loaded / p.total) * 100))
        }
      }
    })
    pdfDoc = await loadingTask.promise
    window.__PDFJS_LOADED__.pdfDocument = pdfDoc
    ownLoaded.value = true
    loadingProgress.value = 100
  } catch (err) {
    const msg = err?.message ?? String(err)
    // errorMsg.value = 'PDF 初始化失败：' + msg
    // throw err
  } finally {
    setTimeout(() => { if (loadingProgress.value >= 100) loadingProgress.value = 0 }, 300)
    loading.value = false
  }
}

/* helper: 尝试重建 worker/module 并重试渲染（用于 sendWithPromise null 的场景） */
async function retryInitAndRender(pageNum) {
  console.warn('尝试重建 pdf.js 模块并重试渲染...')
  try {
    // 清理
    if (lastRenderTask && lastRenderTask.cancel) {
      try { lastRenderTask.cancel() } catch (_) { }
      lastRenderTask = null
    }
    if (loadingTask && loadingTask.destroy) {
      try { loadingTask.destroy() } catch (_) { }
      loadingTask = null
    }
    if (ownLoaded.value && pdfDoc && pdfDoc.destroy) {
      try { pdfDoc.destroy() } catch (_) { }
      pdfDoc = null
      ownLoaded.value = false
    }
    if (window.__PDFJS_LOADED__) {
      window.__PDFJS_LOADED__.pdfDocument = null
    }
    // 重新 ensure 并渲染
    await ensureModuleAndDoc()
    await renderAllPages()
  } catch (err) {
    console.error('retryInitAndRender 失败：', err)
    throw err
  }
}

/* renderSinglePage（渲染单页到对应 canvas） */
async function renderSinglePage(pageNum = 1) {
  if (!pdfDoc) return
  if (!pageNum || pageNum < 1 || pageNum > pdfDoc.numPages) return

  const idx = pageNum - 1
  const canvas = canvasRefs.value[idx]
  if (!canvas) {
    // canvas 未创建（可能尚未挂载），等待下一个 tick 再试一次
    await nextTick()
    if (!canvasRefs.value[idx]) return
  }

  if (lastRenderTask && lastRenderTask.cancel) {
    try { lastRenderTask.cancel() } catch (e) { }
    lastRenderTask = null
  }

  try {
    loading.value = true
    const page = await pdfDoc.getPage(pageNum)
    const viewport = page.getViewport({ scale: localScale.value })

    const targetCanvas = canvasRefs.value[idx]
    if (!targetCanvas) return
    const ctx = targetCanvas.getContext('2d')

    const outputScale = (props.enableHighDPI && window.devicePixelRatio) ? window.devicePixelRatio : 1
    targetCanvas.width = Math.floor(viewport.width * outputScale)
    targetCanvas.height = Math.floor(viewport.height * outputScale)
    targetCanvas.style.width = Math.floor(viewport.width) + 'px'
    targetCanvas.style.height = Math.floor(viewport.height) + 'px'

    if (outputScale !== 1) ctx.setTransform(outputScale, 0, 0, outputScale, 0, 0)
    else ctx.setTransform(1, 0, 0, 1, 0, 0)

    const renderContext = { canvasContext: ctx, viewport }
    const renderTask = page.render(renderContext)
    lastRenderTask = renderTask
    await renderTask.promise

    canvasReadyFlags.value[idx] = true
    totalPages.value = pdfDoc.numPages
    emit('loaded', pdfDoc.numPages)
  } catch (err) {
    const msg = err?.message ?? ''
    if (msg.includes('sendWithPromise') || msg.includes('worker') || /Cannot read properties of null/i.test(msg)) {
      console.warn('检测到 sendWithPromise/worker 相关错误，尝试重试：', msg)
      try {
        await retryInitAndRender(pageNum)
        return
      } catch (retryErr) {
        errorMsg.value = '渲染失败（重试后仍失败）：' + (retryErr?.message ?? retryErr)
        // TODO 异常处理
        // emit('error', retryErr)
        console.error('重试失败', retryErr)
      }
    } else {
      if (err && err.name === 'RenderingCancelledException') {
        // ignore
      } else {
        canvasReadyFlags.value[idx] = false
        // TODO 异常处理
        // errorMsg.value = '渲染失败：' + (err?.message ?? err)
        // emit('error', err)
        console.error('renderSinglePage error', err)
      }
    }
  } finally {
    loading.value = false
  }
}

/* renderAllPages: 顺序渲染每一页（避免并发过多） */
async function renderAllPages() {
  if (!pdfDoc) return
  const n = pdfDoc.numPages
  totalPages.value = n
  // 初始化 refs 和 flags
  canvasRefs.value = new Array(n).fill(null)
  canvasReadyFlags.value = new Array(n).fill(false)
  await nextTick()
  // 顺序渲染每页（可改为并发，但顺序更稳妥）
  for (let i = 1; i <= n; i++) {
    // 在渲染过程中如果组件卸载或切换 source，则中断
    if (!pdfDoc || totalPages.value !== n) break
    try {
      await renderSinglePage(i)
    } catch (err) {
      console.error('renderAllPages renderSinglePage error', err)
    }
  }
}

/* load document if needed（保持和上文一致，渲染策略调整为 renderAllPages）*/
async function loadDocumentIfNeeded(newSrc) {
  if (injectedPdfDocRef && (injectedPdfDocRef.value ?? injectedPdfDocRef)) {
    pdfDoc = injectedPdfDocRef.value ?? injectedPdfDocRef
    pdfModule = injectedPdfModuleRef ? (injectedPdfModuleRef.value ?? injectedPdfModuleRef) : null
    totalPages.value = pdfDoc.numPages
    // 初始化 canvas placeholders
    canvasRefs.value = new Array(pdfDoc.numPages).fill(null)
    canvasReadyFlags.value = new Array(pdfDoc.numPages).fill(false)
    emit('loaded', pdfDoc.numPages)
    return
  }

  if (pdfDoc && props.src === newSrc) {
    totalPages.value = pdfDoc.numPages
    emit('loaded', pdfDoc.numPages)
    return
  }

  if (loadingTask && loadingTask.destroy) {
    try { loadingTask.destroy() } catch (e) { }
    loadingTask = null
  }

  try {
    loading.value = true
    await ensureModuleAndDoc()
    if (pdfDoc) {
      totalPages.value = pdfDoc.numPages
      // 初始化 canvas placeholders
      canvasRefs.value = new Array(pdfDoc.numPages).fill(null)
      canvasReadyFlags.value = new Array(pdfDoc.numPages).fill(false)
      emit('loaded', pdfDoc.numPages)
    }
  } catch (err) {
    emit('error', err)
    console.error('loadDocumentIfNeeded error', err)
  } finally {
    loading.value = false
  }
}

/* navigation & zoom（操作 local state 并触发滚动到对应 canvas） */
function prevPage() {
  if (localPage.value > 1) {
    localPage.value -= 1
    scrollToPage(localPage.value - 1)
  }
}
function nextPage() {
  if (localPage.value < totalPages.value) {
    localPage.value += 1
    scrollToPage(localPage.value - 1)
  }
}
function zoomIn() {
  const next = Math.min(3, +(localScale.value + 0.1).toFixed(2))
  localScale.value = next
  // 重新渲染所有已渲染的页面，以应用新的 scale
  renderAllPages().catch(e => console.error(e))
}
function zoomOut() {
  const next = Math.max(0.5, +(localScale.value - 0.1).toFixed(2))
  localScale.value = next
  renderAllPages().catch(e => console.error(e))
}

/* scrollToPage: 平滑滚动到某页 canvas（index 从0开始） */
function scrollToPage(index) {
  const el = canvasRefs.value[index]
  if (el && el.scrollIntoView) {
    // 如果 canvas 被包在 .page-container，我们希望滚动到容器顶部
    const container = el.parentElement || el
    container.scrollIntoView({ behavior: 'smooth', block: 'start' })
  } else {
    // 兜底：滚动到父容器并尝试定位
    const wrap = document.querySelector('.scrollable-area')
    if (wrap) {
      wrap.scrollTo({ top: 0, behavior: 'smooth' })
    }
  }
}

/* search（逐页扫描） */
async function searchPDF() {
  if (!pdfDoc || !searchText.value.trim()) return
  try {
    loading.value = true
    searchResults.value = []
    currentResultIndex.value = -1
    const q = searchText.value.trim().toLowerCase()

    for (let i = 1; i <= pdfDoc.numPages; i++) {
      const page = await pdfDoc.getPage(i)
      const content = await page.getTextContent()
      const text = content.items.map(item => item.str).join(' ')
      const idx = text.toLowerCase().indexOf(q)
      if (idx !== -1) {
        const snippetStart = Math.max(0, idx - 20)
        const snippet = text.substring(snippetStart, idx + q.length + 40).replace(/\s+/g, ' ')
        searchResults.value.push({ page: i - 1, snippet })
      }
    }
    if (searchResults.value.length) {
      currentResultIndex.value = 0
      // 自动跳转到第一个结果（滚动）
      jumpToResult(0)
    }
  } catch (err) {
    errorMsg.value = '搜索失败: ' + (err?.message ?? err)
    console.error('searchPDF error', err)
  } finally {
    loading.value = false
  }
}

function jumpToResult(index) {
  const r = searchResults.value[index]
  if (r) {
    currentResultIndex.value = index
    localPage.value = r.page + 1
    // 滚动而不是重新渲染（页面应该已经预渲染）
    scrollToPage(r.page)
  }
}

function highlightMatch(text) {
  if (!searchText.value) return text
  const q = searchText.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
  const re = new RegExp(`(${q})`, 'ig')
  return text.replace(re, '<mark class="search-highlight">$1</mark>')
}

function resetSearch() {
  searchText.value = ''
  searchResults.value = []
  currentResultIndex.value = -1
}

/* reload */
async function reloadPDF() {
  errorMsg.value = ''
  // reset flags & refs
  canvasRefs.value = []
  canvasReadyFlags.value = []
  if (loadingTask && loadingTask.destroy) {
    try { loadingTask.destroy() } catch (e) { }
    loadingTask = null
  }
  if (ownLoaded.value && pdfDoc && pdfDoc.destroy) {
    try { pdfDoc.destroy() } catch (e) { }
    pdfDoc = null
    ownLoaded.value = false
    if (window.__PDFJS_LOADED__) window.__PDFJS_LOADED__.pdfDocument = null
  }
  try {
    await loadDocumentIfNeeded(props.src)
    await nextTick()
    // 渲染所有页面
    await renderAllPages()
    // 滚动到当前页
    await nextTick()
    scrollToPage(localPage.value - 1)
  } catch (err) {
    console.error('reloadPDF error', err)
  }
}

/* lifecycle */
onMounted(async () => {
  try {
    await loadDocumentIfNeeded(props.src)
    await nextTick()
    // render all pages (preload)
    await renderAllPages()
    // initial scroll to page
    await nextTick()
    scrollToPage(localPage.value - 1)
  } catch (err) {
    errorMsg.value = '初始化失败: ' + (err?.message ?? err)
    console.error('onMounted error', err)
  }
})

onBeforeUnmount(() => {
  if (lastRenderTask && lastRenderTask.cancel) {
    try { lastRenderTask.cancel() } catch (e) { }
  }
  if (loadingTask && loadingTask.destroy) {
    try { loadingTask.destroy() } catch (e) { }
  }
  if (ownLoaded.value && pdfDoc && pdfDoc.destroy) {
    try { pdfDoc.destroy() } catch (e) { }
  }
})

/* watchers: respond to outer changes (src/page/scale) */
watch(() => props.src, async (newSrc, oldSrc) => {
  if (newSrc === oldSrc) return
  resetSearch()
  canvasRefs.value = []
  canvasReadyFlags.value = []
  localPage.value = props.page || 1
  localScale.value = props.scale || 1.50
  await reloadPDF()
})

watch(localPage, (v) => {
  emit('update:page', v)
})

watch(localScale, (v) => {
  emit('update:scale', v)
})

/* utils */
function setCanvasRef(el, index) {
  // 当 el 为 null 时表示该元素被卸载
  if (!Number.isInteger(index)) return
  if (!canvasRefs.value) canvasRefs.value = []
  canvasRefs.value[index] = el || null
}

function normalizeHeightProp(val) {
  if (typeof val === 'number') return `${val}px`
  if (typeof val === 'string') {
    const trimmed = val.trim()
    // 若已经以 calc( 开头 或 含单位 或 含百分号，则直接返回
    if (/^calc\(/i.test(trimmed) || /(vh|px|%|em|rem)$/i.test(trimmed)) return trimmed
    // 如果包含运算符，包装为 calc(...)
    if (/[-+*/]/.test(trimmed)) return `calc(${trimmed})`
    // 若是纯数字字符串，转为 px
    if (/^\d+$/.test(trimmed)) return `${trimmed}px`
    // 兜底，直接返回
    return trimmed
  }
  // fallback
  return '900px'
}

/* 结束：请确保已在项目中按说明放置 pdf.mjs / pdf.worker.mjs，并在 index.html 引入 fontawesome */

</script>

<style scoped
  lang="scss">
  .vue-pdf-container {
    position: relative;
    width: 100%;
    margin-top:0px;
    background: #ffffff;
    border-radius: 10px;
    overflow: hidden;

    &.is-loading {
      .pdf-canvas {
        filter: blur(1px);
      }
    }
  }

  /*
    Toolbar: EUI-inspired visual adjustments
    - 扁平、轻量、分区（divider）
    - 按钮为扁平边框风格，主操作为蓝色填充
    - 输入框圆角、白底、微阴影
  */
  .pdf-toolbar {
    display: flex;
    gap: 12px;
    border-radius: 10px;
    align-items: center;
    padding: 10px 16px;
    flex-wrap: nowrap;
    min-height: 48px;
    color: #1f2a37;
  }

  .toolbar-group {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  /* 分割线（竖向） */
  .toolbar-divider {
    width: 1px;
    height: 26px;
    background: rgba(37, 56, 88, 0.06);
    border-radius: 1px;
  }

  .toolbar-right-spacer {
    flex: 1 1 auto;
  }

  /* Page info */
  .page-info {
    font-size: 13px;
    color: #253858; /* deeper text */
    font-weight: 600;
    min-width: 140px;
    text-align: center;
    display: inline-flex;
    align-items: center;
    justify-content: center;

    .progress-text {
      color: #0b66b2;
      margin-left: 8px;
      font-size: 12px;
      font-weight: 500;
    }
  }

  /* Scale area */
  .scale-controls {
    background: transparent;
    padding: 4px 8px;
    border-radius: 8px;
  }

  .scale-info {
    font-size: 13px;
    color: #253858;
    font-weight: 600;
    padding: 0 6px;
  }

  /* Search box tweaks: 用 ::v-deep 定向到 el-input 内部结构 */
  .search-box {
    max-width: 420px;
    min-width: 160px;
    display: flex;
    align-items: center;
    gap: 8px;
  }

  .search-box ::v-deep .el-input__inner {
    background: #fff;
    /* border: 1px solid rgba(37, 56, 88, 0.08); */
    border-radius: 8px;
    padding-left: 10px;
    padding-right: 10px;
    height: 34px;
    box-shadow: none;
    transition: border-color 0.15s ease, box-shadow 0.12s ease;
  }

  .search-box ::v-deep .el-input__inner:focus {
    border-color: rgba(11, 102, 178, 0.95);
    box-shadow: 0 0 0 3px rgba(11, 102, 178, 0.06);
    outline: none;
  }

  /* Button base: flat style like EUI */
  .pdf-toolbar .el-button {
    border-radius: 6px;
    background: transparent;
    border: 1px solid transparent;
    color: #1f6feb;
    padding: 6px 8px;
    height: 34px;
    min-width: 34px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
    box-shadow: none;
  }

  .pdf-toolbar .el-button .fa-solid {
    font-size: 14px;
  }

  .pdf-toolbar .el-button.flat-btn {
    color: #1f6feb;
    background: transparent;
    border-color: rgba(37, 56, 88, 0.06);
  }

  .pdf-toolbar .el-button.flat-btn:hover:not(:disabled) {
    background: rgba(31, 111, 235, 0.06);
    border-color: rgba(31, 111, 235, 0.16);
  }

  /* primary action - reload */
  .pdf-toolbar .el-button.primary-btn {
    background: #0079bf;
    color: #fff;
    border-color: transparent;
    padding: 6px 12px;
    height: 36px;
    font-weight: 600;
  }

  .pdf-toolbar .el-button.primary-btn:hover:not(:disabled) {
    background: #0061a3;
  }

  /* disabled look */
  .pdf-toolbar .el-button:disabled {
    opacity: 0.45;
    cursor: not-allowed;
    background: transparent;
  }

  /* popover result button more subtle */
  .search-result-btn {
    background: #fff;
    border: 1px solid rgba(37, 56, 88, 0.06);
    color: #0b66b2;
    padding: 6px 10px;
  }

  /* content area */
  .scrollable-area {
    position: relative;
    padding: 16px;
    border-right: 1px solid #f0f1f1;
    background: #fafafa;
    overflow: auto;
  }

  .canvas-wrap {
    display: flex;
    flex-direction: column;
    gap: 18px;
    align-items: center;
    padding: 16px;
    min-height: 160px;
    width: 100%;
  }

  .page-container {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    position: relative;
  }

  .pdf-canvas {
    display: block;
    background: #ffffff;
    border-radius: 6px;
    box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
    max-width: 100%;
    height: auto;
  }

  /* placeholders / overlays */
  .canvas-placeholder {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 60%;
    z-index: 2;
  }

  .loading-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.6);
    z-index: 50;
  }

  .loading-inner {
    display: flex;
    gap: 8px;
    align-items: center;
    color: #222;
    font-size: 14px;
  }

  .error-overlay {
    position: absolute;
    left: 16px;
    right: 16px;
    top: 60px;
    z-index: 60;
  }

  /* search results popover */
  .result-list {
    max-height: 260px;
    overflow: auto;
  }

  .result-item {
    padding: 8px;
    border-bottom: 1px dashed #eee;
    cursor: pointer;
  }

  .result-item.active {
    background: #f0f6ff;
  }

  .result-page {
    font-size: 12px;
    color: #6b7280;
    margin-bottom: 4px;
  }

  .result-snippet {
    font-size: 13px;
    color: #111827;
  }

  .search-highlight {
    background: #fff20033;
    padding: 0 2px;
    border-radius: 2px;
  }

  /* responsive */
  @media (max-width: 640px) {
    .pdf-toolbar {
      padding: 8px;
      gap: 8px;
      flex-wrap: wrap;
    }

    .page-info {
      min-width: 90px;
      font-size: 12px;
    }

    .toolbar-divider {
      display: none;
    }

    .toolbar-right-spacer {
      display: none;
    }
  }
</style>